/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#ifndef GIMI_MESSAGEBUFFER_H
#define GIMI_MESSAGEBUFFER_H

#include <queue>
#include <map>

#include "gimiutils.h"
#include "gimimessage.h"

namespace gimi
{

/**
 * \class GIMIMessageQueue
 *
 * A FIFO-type container for messages.
 *
 */
class GIMIMessageQueue
{
  public:
    GIMIMessageQueue(const int minType,
                     const int mSize);
    
    ~GIMIMessageQueue();

    int pushMessage(GIMIMessage *msg,
                    const bool copyMessage);
    int popMessage(GIMIMessage **msg);

    int pop();

    int getMaxSize() const;
    int getCurSize() const;

    int getType() const;

    bool setMaxSize(const int mSize);

  private:
    std::queue< GIMIMessage* > msgQueue;
    int minorType;
    int maxSize;
};

typedef std::map< int, GIMIMessageQueue > t_msgQueueMap;
typedef t_msgQueueMap::iterator t_msgQueueMapIterator;
typedef t_msgQueueMap::const_iterator t_msgQueueMapConstIterator;

typedef std::pair< int, GIMIMessageQueue > t_msgQueuePair;

/**
 * \class GIMIMessageMajorMap
 *
 * Contains messagequeues for all minor types.
 * If ignoreMinorType is set, then messages from all minor types are inserted
 * to a single messagequeue.
 */
class GIMIMessageMajorMap
{
  public:
    GIMIMessageMajorMap(const int majType,
                        const int defQueueSize);
    
    ~GIMIMessageMajorMap();

    /**
     * Inserts message to specified minor-type queue.
     * If queue does not exist, it will be created.
     *
     * \param[in] msg Messagepointer to insert.
     * \param[in] minorType Type of queue to use.
     * \param[in] copyMessage If true, given message is copied instead of moved.
     * \return GIMIMSG_OK always.
     */
    int pushMessage(GIMIMessage *msg,
                    const int minorType,
                    const bool copyMessage);

    /**
     * Pops message from specified minor-type queue.
     * \warning If ignoreminor is set, minorType argument is ignored.
     *
     * \param[out] msg
     * \param[in] minorType
     * \return GIMIMSG_OK If message successfully popped, GIMIMSG_NOMSGS if
     * queue was empty or not found.
     */
    int popMessage(GIMIMessage **msg,
                   const int minorType = GIMI_IGNOREMINORTYPE);

    /**
     * Pops first encountered message from given minor-type range.
     * 
     * \param[out] msg
     * \param[in] minorTypeMin Lowest accepted minortype
     * \param[in] minorTypeMax Highest accepted minortype
     * \return GIMIMSG_OK If message successfully popped, GIMIMSG_NOMSGS if
     * queue was empty or not found.
     */
    int popMessageRange(GIMIMessage **msg,
                        const int minorTypeMin,
                        const int minorTypeMax);
    
    /**
     * Get queue maximum size from specified minor-type.
     * If minortype does not exist, defaultQueueSize is returned.
     *
     * \param[in] minorType
     * \return < 0 for error, else the maximum size of queue.
     */
    int getMaxSize(const int minorType) const;

    /**
     * Get current queue size from specified minor-type.
     * If minortype does not exist, 0 is returned.
     *
     * \param[in] minorType
     * \return < 0 for error, else current size of queue.
     */
    int getCurSize(const int minorType) const;

    /**
     * Returns number of messages in all minor-queues of this type.
     */
    int getTotalSize() const;

    int getType() const;

    bool setMaxSize(const int minorType,
                    const int mSize);

  private:
    std::map< int, GIMIMessageQueue > msgQueueMap;
    int majorType;
    int messages;
    int defaultQueueSize;
};

typedef std::map< int, GIMIMessageMajorMap > t_msgMajorMap;
typedef t_msgMajorMap::iterator t_msgMajorMapIterator;
typedef t_msgMajorMap::const_iterator t_msgMajorMapConstIterator;

typedef std::pair< int, GIMIMessageMajorMap > t_msgMajorMapPair;

/**
 * \class GIMIMessageBuffer
 *
 * Wraps the message buffering structure. Each type (major+minor) has it's own
 * buffer.
 *
 */
class GIMIMessageBuffer
{
  public:
    GIMIMessageBuffer(const int defQueueSize);
    ~GIMIMessageBuffer();

    /**
     * Inserts the given messagepointer to end of given buffer.
     * If queue does not exist, it will be created.
     * \warning The pointer is inserted onto buffer, content not copied!
     *
     * \param[in] msg Message itself.
     * \param[in] majorType Major-type of message.
     * \param[in] minorType Minor-type of message.
     * \param[in] copyMessage Indicates if given messages data should be
     * copied rather than just assigned.
     * \return GIMIMSG_OK always
     */
    int pushMessage(GIMIMessage *msg,
                    const int majorType,
                    const int minorType,
                    const bool copyMessage = false);

    /**
     * Returns the first message in defined buffer and removes it from
     * the buffer. If types are not defined, returns first found message.
     * 
     * \param[out] msg Pointer in which pointer to message is written.
     * \param[in] majorType Major-type of message.
     * \param[in] minorType Minor-type of message.
     * \return GIMIMSG_OK If message was popped successfully, GIMIMSG_NOMSGS
     * if type was not found or queue(s) was empty.
     */
    int popMessage(GIMIMessage **msg,
                   const int majorType /* = GIMI_IGNOREMAJORTYPE */,
                   const int minorType /* = GIMI_IGNOREMINORTYPE */);

    int popMessageRange(GIMIMessage **msg,
                        const int majorTypeMin,
                        const int majorTypeMax,
                        const int minorTypeMin,
                        const int minorTypeMax);
    
    /**
     * Returns maximum queue size for defined major+minor.
     * 
     * \return 0 if type not found, else maxsize of given queue.
     */
    int getQueueMaxSize(const int majorType,
                        const int minorType) const;


    /**
     * Returns current queue size for defined major+minor.
     * \warning If buffer is set to ignore major/minor types, given arguments
     * are ignored and given value is from the used major or generic buffer.
     *
     * \return 0 if type not found, else size of given queue.
     */
    int getQueueCurSize(const int majorType,
                        const int minorType) const;

    /**
     * Returns total number of messages in all major/minor queues (or the
     * generic queue, if used)
     */
    int getTotalSize() const;

    /**
     * Sets maximum queue size for defined major+minor.
     * Size must be > 0.
     *
     * \return Boolean for success.
     */
    bool setQueueMaxSize(const int majorType,
                         const int minorType,
                         const int mSize);

    /**
     * Clears all messages.
     */
    void clearBuffer();
    
  private:
    // Sorted by major type.
    std::map< int, GIMIMessageMajorMap > msgMajorMap;
    int messages;
    int defaultQueueSize;
};

} // end namespace gimi

#endif // GIMI_MESSAGEBUFFER_H
